package com.fly.core.aspect;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import com.fly.core.annotation.ResourceLimit;
import com.fly.core.annotation.Status;
import com.fly.core.cache.SimpleCacheManager;
import com.fly.core.utils.HttpRequestUtils;
import com.fly.show.model.JsonResult;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * aop 资源调用限制处理<br>
 * 例如：10分钟内登录失败超3次，30分钟后再试<br>
 * 或 24小时内短信发送成功超100次，24小时后再试
 * 
 * @author 00fly
 * @version [版本号, 2022年3月18日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Slf4j
@Aspect
@Component
public class ResourceLimitAspect
{
    /**
     * around
     * 
     * @param joinPoint
     * @param limit 注解一旦定义，就得在pointcut里面引用，不然报0 formal unbound in pointcut 错误
     * @throws Throwable
     * @see [类、类#方法、类#成员]
     */
    @Around("@annotation(limit)")
    public Object around(ProceedingJoinPoint joinPoint, ResourceLimit limit)
        throws Throwable
    {
        log.info("running @Around, ResourceLimit： {}", limit);
        String className = joinPoint.getTarget().getClass().getSimpleName();
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = new StringBuffer(className).append(".").append(method.getName()).toString();
        HttpServletRequest request = HttpRequestUtils.getHttpServletRequest();
        
        // 此处限定了web请求，且与userName绑定(硬编码)，业务处理不够通用，待优化
        if (request != null)
        {
            String userName = request.getParameter("userName");
            String key = userName + "_" + methodName;
            Integer count = SimpleCacheManager.get(key);
            if (count != null && count > limit.maxCount())
            {
                String msg = String.format("您请求过于频繁，请%s分钟后再试", limit.next());
                return JsonResult.error(msg);
            }
        }
        Object object = joinPoint.proceed();
        if (object instanceof JsonResult)
        {
            JsonResult<?> result = (JsonResult<?>)object;
            log.info("running @Around, result： {}", result);
            if (request != null)
            {
                String userName = request.getParameter("userName");
                String key = userName + "_" + methodName;
                Integer count = SimpleCacheManager.get(key);
                // 成功/失败次数+1
                if (limit.condition() == Status.SUCCESS && result.isSuccess())
                {
                    count = (null == count ? 1 : count + 1);
                    SimpleCacheManager.set(key, count, limit.next() * 60);
                }
                if (limit.condition() == Status.FAILURE && !result.isSuccess())
                {
                    count = (null == count ? 1 : count + 1);
                    SimpleCacheManager.set(key, count, limit.next() * 60);
                }
            }
        }
        return object;
    }
    
    @AfterThrowing("@annotation(limit)")
    public void afterThrowing(JoinPoint joinPoint, ResourceLimit limit)
    {
        log.info("running @AfterThrowing");
    }
}
